use crate::common;
use crate::common::db::DatabaseState;
use crate::common::ApiResult;
use crate::router::user;
use axum::routing::{get, post};
use axum::{
    extract::{Json, Query, State},
    http::header::HeaderMap,
    Router,
};
use mysql::prelude::*;
use mysql::*;
use serde::{Deserialize, Serialize};
use std::sync::{Arc, Mutex};

pub async fn route(
    router: Router<Arc<Mutex<DatabaseState>>>,
    path: String,
) -> Router<Arc<Mutex<DatabaseState>>> {
    router
        .route(format!("{}/login", path).as_str(), post(user::login))
        .route(format!("{}/logout", path).as_str(), post(user::logout))
        .route(format!("{}/info", path).as_str(), get(user::info))
}

/**
 * 登录参数
 */
#[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct LoginParam {
    username: String,
    password: String,
}

/**
 * 登录结果
 */
#[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct LoginData {
    token: String,
}

/**
 * 登录
 */
pub async fn login(
    State(db_state): State<Arc<Mutex<common::db::DatabaseState>>>,
    body: String,
) -> Json<ApiResult<LoginData>> {
    let param: Json<LoginParam> = Json::from_bytes(body.as_bytes()).unwrap();
    let mut conn = db_state.lock().unwrap().pool.get_conn().unwrap();
    let result: Option<(u32, String, u32)> = conn.exec_first(
        "select`id`,md5(concat(unix_timestamp(now()),`name`,rand()))`token`,unix_timestamp(now())+600`expire`from`user`where`name`=:username and`password`=:password",
        params! {"username" => &param.username,"password" => &param.password},
    ).unwrap();
    match result {
        Some((id, token, expire)) => {
            match conn.exec_iter(
                "insert into`token`(`token`,`expire`,`user_id`)values(:token,:expire,:id)",
                params! {"id" => &id,"token" => &token,"expire" => &expire},
            ) {
                Ok(_) => Json(ApiResult {
                    code: common::API_SUCCESS,
                    data: Some(LoginData { token }),
                    message: None,
                }),
                Err(error) => Json(ApiResult {
                    code: common::ERROR_BAD_TOKEN,
                    data: None,
                    message: Some(format!("分配令牌错误: {}", error.to_string())),
                }),
            }
        }
        _ => Json(ApiResult {
            code: common::ERROR_BAD_AUTH,
            data: None,
            message: Some("账号密码错误".to_string()),
        }),
    }
}

#[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct UserInfoParam {
    token: String,
}

#[derive(Deserialize, Serialize, PartialEq, Debug)]
pub struct UserInfoData {
    roles: Vec<String>,
    introduction: String,
    avatar: String,
    name: String,
}

pub async fn info(
    Query(query): Query<UserInfoParam>,
    State(db_state): State<Arc<Mutex<common::db::DatabaseState>>>,
) -> Json<ApiResult<UserInfoData>> {
    let mut conn = db_state.lock().unwrap().pool.get_conn().unwrap();
    conn.exec_drop(
        "delete from`token`where`expire`<unix_timestamp(now())-600",
        (),
    )
    .unwrap();
    let result: Option<(String, String, String)> = conn.exec_first(
        "select`introduction`,`avatar`,`name`from`user`join`token`on`user`.`id`=`token`.`user_id`where`token`=:token",
        params! {"token" => &query.token},
    ).unwrap();
    match result {
        Some((introduction, avatar, name)) => Json(ApiResult {
            code: 20000,
            data: Some(UserInfoData {
                roles: vec!["admin".to_string()],
                introduction,
                avatar,
                name,
            }),
            message: None,
        }),
        _ => Json(ApiResult {
            code: 50008,
            data: None,
            message: Some("Login failed, unable to get user details.".to_string()),
        }),
    }
}

pub async fn logout(headers: HeaderMap) -> Json<ApiResult<String>> {
    //"x-token": "admin"
    println!("headers => {:?}", headers);
    Json(ApiResult {
        code: 20000,
        data: None,
        message: Some("success".to_string()),
    })
}
